package com.ruge.ruge_novel_front.tool;

import com.ruge.ruge_novel_front.config.CrawlerConfig;
import com.ruge.ruge_novel_front.dao.RugeNovelChapterDao;
import com.ruge.ruge_novel_front.dao.RugeNovelDao;
import com.ruge.ruge_novel_front.dao.RugeNovelRuleDao;
import com.ruge.ruge_novel_front.model.RugeNovelChapterModel;
import com.ruge.ruge_novel_front.model.RugeNovelModel;
import com.ruge.ruge_novel_front.model.RugeNovelRuleModel;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.stereotype.Repository;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 创建人 ：爱丽丝、如歌
 * 创建时间 ：2019/11/15  19:21
 * 描述 ： 爬虫工具类
 */
@Repository
public class CrawlerTool {

    @Autowired
    private RugeNovelRuleDao novelRuleDao;
    @Autowired
    private RugeNovelChapterDao novelChapterDao;
    @Autowired
    private CrawlerConfig config;
    @Autowired
    private RugeNovelDao novelDao;

    private static CrawlerConfig crawlerConfig;
    private static RugeNovelRuleDao rugeNovelRuleDao;
    private static RugeNovelChapterDao rugeNovelChapterDao;
    private static RugeNovelDao rugeNovelDao;

    @PostConstruct
    public void init() {
        rugeNovelRuleDao = novelRuleDao;
        rugeNovelChapterDao = novelChapterDao;
        crawlerConfig = config;
        rugeNovelDao = novelDao;
    }

    public static String request(RugeNovelRuleModel model) {
        String result = "";
        if ("get".equals(model.getRuleNovelSearchRequest().toLowerCase())) {
            result = get(model.getSourceUrl(), model.getCharset());
        } else if ("post".equals(model.getRuleNovelSearchRequest().toLowerCase())) {
            result = post(model);
        }
        return result;
    }

    public static String get(String url, String charset) {
        CloseableHttpResponse response = null;
        String result = "";
        try {
            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            HttpGet httpGet = new HttpGet(url);
            /**
             * 设置请求和传输超时时间
             */
            RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(crawlerConfig.getConnectionRequestTimeout()).setSocketTimeout(crawlerConfig.getSocketTimeout()).setConnectTimeout(crawlerConfig.getConnectTimeout()).build();
            httpGet.setConfig(requestConfig);
            response = httpClient.execute(httpGet);
            if (StringUtils.isBlank(charset)) {
                charset = "utf-8";
            }
            result = EntityUtils.toString(response.getEntity(), charset);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return result;
    }

    public static String post(RugeNovelRuleModel model) {
        CloseableHttpResponse response = null;
        String result = "";
        List<NameValuePair> list = new ArrayList<>();
        try {
            model.getNovelParams().forEach((k, v) -> {
                list.add(new BasicNameValuePair(k, v));
            });
            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            HttpPost http = new HttpPost(model.getRuleNovelSearchUrl());
            http.setEntity(new UrlEncodedFormEntity(list, model.getCharset()));
            http.setHeader("User-Agent", model.getHttpUserAgent());
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(3000).setConnectTimeout(3000).build();
            http.setConfig(requestConfig);
            response = httpClient.execute(http);
            result = EntityUtils.toString(response.getEntity(), model.getCharset());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return result;
    }

    /**
     * 小说搜索环节显示列表
     *
     * @param rugeNovelRuleModels
     * @param map
     * @return
     */
    public static List<RugeNovelModel> getRugeNovel(List<RugeNovelRuleModel> rugeNovelRuleModels, Map<String, String> map) {
        List<RugeNovelModel> result = new ArrayList<>();
        String baseUrl = rugeNovelRuleModels.get(0).getSourceUrl();
        for (int i = 0; i < rugeNovelRuleModels.size(); i++) {
            rugeNovelRuleModels.get(i).setNovelParams(map);
            String request = request(rugeNovelRuleModels.get(i));
            Document document = Jsoup.parse(request);
            Elements elements = null;
            String ruleNovelSearchResult = rugeNovelRuleModels.get(i).getRuleNovelSearchResult();
            elements = document.select(ruleNovelSearchResult);
            for (int j = 0; j < elements.size(); j++) {
                RugeNovelModel rugeNovelModel = new RugeNovelModel();
                rugeNovelModel.setNovelType(elements.get(j).select(rugeNovelRuleModels.get(i).getRuleNovelSearchResultType()).text());
                rugeNovelModel.setNovelName(elements.get(j).select(rugeNovelRuleModels.get(i).getRuleNovelSearchResultName()).text());
                String novelUrl = elements.get(j).select(rugeNovelRuleModels.get(i).getRuleNovelSearchResultName()).attr("href");
                rugeNovelModel.setNovelURL(StringToUrl(baseUrl, novelUrl));
                rugeNovelModel.setLastChapter(elements.get(j).select(rugeNovelRuleModels.get(i).getRuleNovelSearchResultLastName()).text());
                String lastUrl = elements.get(j).select(rugeNovelRuleModels.get(i).getRuleNovelSearchResultLastName()).attr("href");
                rugeNovelModel.setLastURL(StringToUrl(baseUrl, lastUrl));
                rugeNovelModel.setNovelAuthor(elements.get(j).select(rugeNovelRuleModels.get(i).getRuleNovelSearchResultAuthor()).text());
                rugeNovelModel.setLastTime(elements.get(j).select(rugeNovelRuleModels.get(i).getRuleNovelSearchResultLastName()).text());
                rugeNovelModel.setSourceId(rugeNovelRuleModels.get(i).getId());
                rugeNovelModel.setNovelStatus(Jsoup.parse(get(rugeNovelModel.getNovelURL(), rugeNovelRuleModels.get(i).getCharset())).select(rugeNovelRuleModels.get(i).getRuleNovelStatus()).text());
                rugeNovelModel.setNovelDesc(Jsoup.parse(get(rugeNovelModel.getNovelURL(), rugeNovelRuleModels.get(i).getCharset())).select(rugeNovelRuleModels.get(i).getRuleNovelDesc()).text());
                rugeNovelModel.setNovelCover(StringToUrl(baseUrl,Jsoup.parse(get(rugeNovelModel.getNovelURL(), rugeNovelRuleModels.get(i).getCharset())).select(rugeNovelRuleModels.get(i).getRuleCoverUrl()).attr("src")));
                RugeNovelModel tmp = new RugeNovelModel();
                tmp.setNovelName(rugeNovelModel.getNovelName());
                tmp.setNovelURL(rugeNovelModel.getNovelURL());
                tmp.setLastTime(rugeNovelModel.getLastTime());
                Example<RugeNovelModel> example = Example.of(tmp);
                Optional<RugeNovelModel> model = rugeNovelDao.findOne(example);
                if (!model.isPresent()) {
                    rugeNovelDao.saveAndFlush(rugeNovelModel);
                }
                result.add(rugeNovelModel);
            }
        }
        return result;
    }

    /**
     * 获取小说章节
     *
     * @param id 小说主键
     * @return
     */
    public static List<RugeNovelChapterModel> getrugeChapter(int id) {
        Optional<RugeNovelModel> daoById = rugeNovelDao.findById(id);
        RugeNovelModel novelModel = daoById.get();
        String baseUrl = novelModel.getNovelURL();
        Optional<RugeNovelRuleModel> one = rugeNovelRuleDao.findById(novelModel.getSourceId());
        String s = get(baseUrl, one.get().getCharset());
        Document document = Jsoup.parse(s);
        Elements elements = document.select(one.get().getRuleChapterList());
        Integer index = ruleToIndex(one.get().getRuleIndex(), "ruleChapterList");
        List<RugeNovelChapterModel> list = rugeNovelChapterDao.findByNovelId(id);
        for (int i = index; i < elements.size(); i++) {
            RugeNovelChapterModel tmpModel = rugeNovelChapterDao.findByNovelIdAndCharterUrl(id, StringToUrl(one.get().getSourceUrl(), elements.get(i).attr("href")));
            if (tmpModel == null) {
                RugeNovelChapterModel model = new RugeNovelChapterModel();
                model.setCharterName(elements.get(i).text());
                model.setCharterUrl(StringToUrl(one.get().getSourceUrl(), elements.get(i).attr("href")));
                model.setNovelId(id);
                rugeNovelChapterDao.saveAndFlush(model);
                list.add(model);
            }
        }
        return list;
    }

    public static RugeNovelModel crawerChapter(int id) {
        List<RugeNovelChapterModel> result = new ArrayList<>();
        RugeNovelModel novelModel = rugeNovelDao.findById(2).get();
        String baseUrl = novelModel.getNovelURL();
        Optional<RugeNovelRuleModel> one = rugeNovelRuleDao.findById(1);
        String s = get(baseUrl, one.get().getCharset());
        Document document = Jsoup.parse(s);
        Elements elements = null;
        elements = document.select(one.get().getRuleChapterList());
        Integer index = ruleToIndex(one.get().getRuleIndex(), "ruleChapterList");
        List<RugeNovelChapterModel> list = new ArrayList<>();
        List<String> hasFinish = new ArrayList<>();
        for (int i = index; i < elements.size(); i++) {
            if (!hasFinish.contains(StringToUrl(one.get().getSourceUrl(), elements.get(i).attr("href")))) {
                RugeNovelChapterModel model = new RugeNovelChapterModel();
                model.setCharterName(elements.get(i).text());
                model.setCharterUrl(StringToUrl(one.get().getSourceUrl(), elements.get(i).attr("href")));
                hasFinish.add(model.getCharterUrl());
                String context = get(model.getCharterUrl(), one.get().getCharset());
                Document parse = Jsoup.parse(context);
                StringBuffer stringBuffer = new StringBuffer();
                String nextUrl = StringToUrl(one.get().getSourceUrl(), ruleToPage(parse.select(one.get().getRuleChapterUrlNext()).attr("href"), one.get().getRuleNovelPage(), "hasNext"));
                while (ruleToPage(nextUrl, one.get().getRuleNovelPage(), "next").indexOf("_") > -1) {
                    if (crawlerConfig.getContext()) {
                        stringBuffer.append(parse.select(one.get().getRuleNovelContent()).text());
                    }
                    Document fakedocument = Jsoup.parse(get(nextUrl, one.get().getCharset()));
                    nextUrl = StringToUrl(one.get().getSourceUrl(), ruleToPage(fakedocument.select(one.get().getRuleChapterUrlNext()).attr("href"), one.get().getRuleNovelPage(), "hasNext"));
                    if (crawlerConfig.getContext()) {
                        stringBuffer.append(fakedocument.select(one.get().getRuleNovelContent()).text());
                    }
                }
                if (crawlerConfig.getContext()) {
                    model.setCharterContext(stringBuffer.toString());
                }
                model.setCharterPrevUrl(StringToUrl(one.get().getSourceUrl(), ruleToPage(parse.select(one.get().getRuleChapterUrlPrev()).attr("href"), one.get().getRuleNovelPage(), "hasPrev")));
                model.setCharterNextUrl(nextUrl);
                System.out.println(model);
                list.add(model);
            }
        }
        rugeNovelChapterDao.saveAll(list);
        novelModel.setSourceId(one.get().getId());
        Elements meta = document.head().select("meta");
        for (int i = 0; i < meta.size(); i++) {
            String content = meta.get(i).attr("content");
            if (meta.get(i).attr("property").toLowerCase().equals(one.get().getRuleNovelName())) {
                novelModel.setNovelName(content);
            } else if (meta.get(i).attr("property").toLowerCase().equals(one.get().getRuleNovelDesc())) {
                novelModel.setNovelDesc(content);
            } else if (meta.get(i).attr("property").toLowerCase().equals(one.get().getRuleCoverUrl())) {
                novelModel.setNovelCover(content);
            } else if (meta.get(i).attr("property").toLowerCase().equals(one.get().getRuleNovelType())) {
                novelModel.setNovelType(content);
            } else if (meta.get(i).attr("property").toLowerCase().equals(one.get().getRuleNovelAuthor())) {
                novelModel.setNovelAuthor(content);
            } else if (meta.get(i).attr("property").toLowerCase().equals(one.get().getRuleNovelStatus())) {
                novelModel.setNovelStatus(content);
            } else if (meta.get(i).attr("property").toLowerCase().equals(one.get().getRuleNovelLastChapter())) {
                novelModel.setLastChapter(content);
            } else if (meta.get(i).attr("property").toLowerCase().equals(one.get().getRuleNovelLastTime())) {
                novelModel.setLastTime(content);
            }
        }

        novelModel.setChapterModels(result);
        return novelModel;
    }

    public static String StringToUrl(String base, String expand) {
        if (base.endsWith("/") && expand.startsWith("/")) {
            return base.substring(0, base.length() - 1) + expand;
        } else if (!base.endsWith("/") && !expand.startsWith("/")) {
            return base + "/" + expand;
        } else {
            return base + expand;
        }
    }

    public static Integer ruleToIndex(String rule, String key) {

        Map<String, Integer> map = new HashMap<>();
        if (rule.indexOf("|") > 0) {
            String[] split = StringUtils.split(rule, "|");
            for (int i = 0; i < split.length; i++) {
                String[] strings = split[i].split(",");
                map.put(strings[0], Integer.parseInt(strings[1]));
            }
        } else if (rule.indexOf(",") > 0) {
            String[] split = rule.split(",");
            map.put(split[0], Integer.parseInt(split[1]));
        }

        if (map.containsKey(key)) {
            return map.get(key);
        }
        return 0;
    }

    public static String ruleToPage(String base, String rule, String key) {

        Map<String, String> map = new HashMap<>();
        if (rule.indexOf("|") > 0) {
            String[] split = StringUtils.split(rule, "|");
            for (int i = 0; i < split.length; i++) {
                String[] strings = split[i].split(",");
                for (int j = 0; j < strings.length; j++) {
                    if (strings[j].indexOf(key) > 0) {
                        if (strings[j + 1].indexOf("html") > 0) {
                            return base;
                        } else {
                            return "#";
                        }
                    }
                }
            }
        }
        return base;
    }

    public RugeNovelChapterModel getrugeContext(Integer chapterId, Integer type) {
        RugeNovelChapterModel chapterModel = rugeNovelChapterDao.findById(chapterId).get();
        String baseUrl = "";
        if (type == 0) {
            baseUrl = chapterModel.getCharterPrevUrl();
        } else if (type == 1) {
            baseUrl = chapterModel.getCharterNextUrl();
        } else {
            baseUrl = chapterModel.getCharterUrl();
        }
        RugeNovelModel novelModel = rugeNovelDao.findById(chapterModel.getNovelId()).get();
        RugeNovelRuleModel ruleModel = rugeNovelRuleDao.findById(novelModel.getSourceId()).get();
        String s = get(baseUrl, ruleModel.getCharset());
        Document document = Jsoup.parse(s);
        chapterModel.setCharterContext(document.select(ruleModel.getRuleNovelContent()).get(0).text());
        chapterModel.setCharterPrevName(document.select(ruleModel.getRuleChapterUrlPrev()).get(0).text());
        chapterModel.setCharterNextName(document.select(ruleModel.getRuleChapterUrlNext()).get(0).text());
        chapterModel.setCharterPrevUrl(StringToUrl(ruleModel.getSourceUrl(), document.select(ruleModel.getRuleChapterUrlPrev()).get(0).attr("href")));
        chapterModel.setCharterNextUrl(StringToUrl(ruleModel.getSourceUrl(), document.select(ruleModel.getRuleChapterUrlNext()).get(0).attr("href")));
        rugeNovelChapterDao.saveAndFlush(chapterModel);
        return chapterModel;
    }
}
